package codeoffloading;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Calendar;
import java.util.Random;

import utilities.Constants;
import netcom.NetManager;
import codeoffloading.profiling.Profiler;

public class ClassStub {

	private Profiler prof;
	public ExecutableClass classInstance;
	private Object results = null;
	private Random rand;
	int testCases = 0;
	
	
	public ClassStub(ExecutableClass classInstance) {
		this.classInstance = classInstance;
		rand = new Random(Calendar.getInstance().getTimeInMillis());
		prof = new Profiler(classInstance.getClass());
	}


	//used for updating the internal class variables
	//due to remote execution
	public void setClassInstance(ExecutableClass _cla){
		classInstance = _cla;
	}

	public Object executeMethodOnClass(String methodName, Object... params){
		prof.methodCall(methodName);
		boolean localExecution = true;
		//choice if remotize or not
		
		if(localExecution){
			results = this.localExecuteMethodOnClass(methodName, params);
		}else{
			results = this.remoteMethodExecution(methodName, params);
		}
		
		prof.methodCallEnd(methodName);
		
		return results;
		
		
	}
	
	
	private Object remoteMethodExecution(String methodName, Object[] paramas){
		try {
			CodeOffloadingManager.getInstance().executeMethodRemotely(methodName, this, paramas);
			
			//get the whait time based on past method execution
			this.wait(prof.getMethodWaitTime(methodName));
			
			//send server request
		} catch (InterruptedException e) {e.printStackTrace(); }
		
		return results;
	}
	
	
	public void remoteResultsAreComed(Object res){
		this.results = res;
		this.notify();
	}
	
	
	/**
	 * Add difference multiple paramas
	 */
	
	private Object localExecuteMethodOnClass(String methodName, Object[] prm){
		Method toExec = null;
		for(Method m: classInstance.getClass().getMethods()){
			System.out.println(m.getName()+" "+methodName);
			if(m.getName().compareTo(methodName) == 0){
				toExec = m;
				break;
				//return m.invoke(classInstance, params[0]);
			}
		}
		
		System.out.println(toExec);
		try {
		
			//single paramater method
			if(prm.length < 2) return toExec.invoke(classInstance, prm[0]);
			else return toExec.invoke(classInstance, prm);
		} catch (IllegalArgumentException e) { e.printStackTrace();
		} catch (IllegalAccessException e) { e.printStackTrace();
		} catch (InvocationTargetException e) { e.printStackTrace();}

		return null;
	}
	
	
	public boolean isConvenientToRemotize(String methodName){
		if(!NetManager.getInstance().isServerConnected()) return false;
		
		//manage test cases
		if(this.testCases < Constants.TEST_CASESNUM){
			testCases++;
			if(rand.nextInt()%2 == 0) return true;
			else return false;
		}
		
		
		if(prof.offloadDecision(methodName)) return true;
		return false;
		
	}
	
	
	
	
	
	
	
	
	
}
